home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / mpwtools.cpt / misc src / muntar.c < prev    next >
Encoding:
Text File  |  1991-03-11  |  13.8 KB  |  528 lines

  1. /* Copyright 1988, Gail Zacharias.  All rights reserved.
  2.  * Permission is hereby granted to copy, reproduce, redistribute or
  3.  * otherwise use this software provided there is no monetary profit
  4.  * gained specifically from its use or reproduction, it is not sold,
  5.  * rented, traded or otherwise marketed, and this copyright notice 
  6.  * and the software version number is included prominently in any copy
  7.  * made.
  8.  * This is muntar version 1.0.
  9.  *
  10.  * Send comments, suggestions, bug reports (not bloody likely), feature
  11.  * requests, etc. to gz@entity.com.
  12.  *
  13.  * muntar version 1.0.1.
  14.  * Modified by Sak Wathanasin (sw@network-analysis-ltd.co.uk) to compile
  15.  * with MPW 3.1/2.
  16.  *
  17.  */
  18.  
  19. char *Program, *Version = "1.0.1";
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <files.h>
  25. #include <errors.h>
  26.  
  27. /* Sak W mods */
  28. #define MPS 'MPS '
  29. #define TEXT 'TEXT'
  30.  
  31. /* The do {} while(0) is for syntactic reasons. MPW C will optimize it out. */
  32. #define mcopy(dest, src, len) \
  33.   do { char *_dp = (char *)(dest), *_sp = (char *)(src); \
  34.        int _i = (len); \
  35.        while (--_i>=0) *_dp++ = *_sp++; \
  36.   } while(0)
  37.  
  38. struct {
  39.     char name[100];
  40.     char mode[8];
  41.     char uid[8];
  42.     char gid[8];
  43.     char size[12];
  44.     char mtime[12];
  45.     char chksum[8];
  46.     char linkflag;
  47.     char linkname[100];
  48.     char magic[8];
  49.     char uname[32];
  50.     char gname[32];
  51.     char devmajor[8];
  52.     char devminor[8];
  53.     char fill[167];
  54. } tarh;
  55. #define tarsz(n)    (((n)+511L) & -512L)
  56.  
  57. struct {
  58.     unsigned char nlen;
  59.     char name[63];
  60.     FInfo finfo;
  61.     char protected;
  62.     char zero;
  63.     long dflen;
  64.     long rflen;
  65.     long cdate;
  66.     long mdate;
  67.     char unused[29];
  68. } macbinh;
  69.  
  70. #define macbinsz(n)    (((n)+127L) & -128L)
  71.  
  72. FILE *tarf = (FILE *) NULL;
  73.  
  74. #define readblock(buf, size) ((size) == fread((char *) buf, 1, (size), tarf))
  75.  
  76. StringPtr c2pstr();
  77.  
  78. OSType filecreator = MPS;
  79.  
  80. int listonly = 0,
  81.     binary = 0,
  82.     dataonly = 0,
  83.     verbose = 0,
  84.     ignore_cdate = 0,
  85.     ignore_mdate = 0;
  86.  
  87. char **files = (char **) NULL;
  88. int *ffound = (int *) NULL;
  89.  
  90. main (argc, argv)
  91.   char **argv;
  92. {
  93.     Program = argv[0];
  94.     while (--argc > 0 && **++argv == '-') {
  95.       char *argp = &argv[0][1];
  96.       do switch (*argp++) {
  97.         case 'l': verbose = listonly = 1; break;
  98.     case 'v': verbose = 1; break;
  99.         case 'b': binary = 1; break;
  100.         case 'u': dataonly = 1; break;
  101.         case 'm': ignore_mdate = 1; break;
  102.         case 'd': ignore_cdate = 1; break;
  103.         case 'c': if (*argp == '\0') {
  104.                     if (--argc == 0) goto badarg;
  105.                     argp = *++argv;
  106.                   }
  107.           if (*argp == '\0' || strlen(argp) > 4) goto badarg;
  108.           filecreator = 0x20202020L;
  109.                   { char *cp = (char *)&filecreator;
  110.                      while (*argp) *cp++ = *argp++;
  111.                   }
  112.                   break;
  113.         case 'f': if (tarf) goto badarg;
  114.                   if (*argp == '\0') {
  115.                       if (--argc == 0) goto badarg;
  116.                       argp = *++argv;
  117.                   }
  118.                   if (!strcmp(argp, "-")) tarf = stdin;
  119.           else if (!(tarf = fopen(argp, "r")))
  120.               fatal("Cannot open %s for reading.", argp);
  121.           argp = "";
  122.           break;
  123.         default: goto badarg;
  124.       } while (*argp);
  125.     }
  126.     if (!tarf) tarf = stdin;
  127.     if (argc) {
  128.       files = argv;
  129.       files[argc] = (char *) NULL;
  130.       ffound = (int *)malloc((size_t)(argc * sizeof(int)));
  131.       if (!ffound) fatal("Out of memory.");
  132.       while (argc) ffound[--argc] = 0;
  133.     }
  134.     untar();
  135.     if (tarf != stdin) fclose(tarf);
  136.     if (ffound) free(ffound);
  137.     return 0;
  138.  
  139. badarg:
  140.   fprintf(stderr, "muntar Version %s by gz@entity.com.  Usage: \n", Version);
  141.   fprintf(stderr, "%s [-lvubdm] [-c XXXX] [-f tarfile] filenames...\n", Program);
  142.   fprintf(stderr, "Extracts the named files from a tar archive.\n\
  143.  -l = just list file names, do not actually extract any files.\n\
  144.  -v = verbose: list names of files as extract them.\n\
  145.  -u = do not try to detect and handle macbinary files.\n\
  146.  -b = do not convert LF to CR in non-macbinary files.\n\
  147.  -d = do not restore creation dates in macbinary files.\n");
  148.    fprintf(stderr, " -m = do not restore modification dates in macbinary files.\n\
  149.  -c XXXX = set file creator to XXXX (default is MPS) in non-macbinary files.\n\
  150.  -f tarfile = read input from tarfile (instead of standard input).\n\
  151.  -H = show this help message.\n\
  152. If no filenames are specified, all files in the archive will be\n\
  153. extracted (or listed).\n\n\
  154. This is freeware.  If you paid money for this program, you got ripped off.\n");
  155.   return 2;
  156. }
  157.  
  158. untar ()
  159. {
  160.   long size, chksum, hchksum;
  161.   int i;
  162.   while (readblock(&tarh, sizeof(tarh))) {
  163.     for (i=0; i < sizeof(tarh) && tarh.name[i]=='\0'; ++i) ;
  164.     if (i == sizeof(tarh)) break;
  165.     hchksum = untar_number(tarh.chksum);
  166.     for (i=0; i < 8; ++i) tarh.chksum[i] = ' ';
  167.     for (i=chksum=0; i < sizeof(tarh); ++i) chksum += (unsigned char)tarh.name[i];
  168.     if (chksum != hchksum)
  169.       fatal("Bad header checksum (got 0%lo, expected 0%lo) for %s",
  170.             chksum, hchksum, tarh.name);
  171.     switch (tarh.linkflag) {
  172.       case '\0': case '0': untar_file(); break;
  173.       case '1': case '2':  untar_link(); break;
  174.       case '5': untar_directory(); break;
  175.       default: fatal("Unknown header type (0%o)", tarh.linkflag);
  176.     }
  177.   }
  178.   if (!feof(tarf) && ferror(tarf)) readerr();
  179.  
  180.   for(i=0; files[i]; ++i) if (!ffound[i])
  181.     fprintf(stderr,"%s: Warning - File %s not found.\n", Program, files[i]);
  182. }
  183.  
  184. untar_file ()
  185. {
  186.   long fsize = untar_number(tarh.size);
  187.   if (tarh.name[strlen(tarh.name)-1] == '/') return untar_directory();
  188.   unix2mac(tarh.name);
  189.   if (!dataonly && chkmacbin(fsize)) untar_macbin(fsize);
  190.   else untar_data(fsize);
  191. }
  192.  
  193. chkmacbin (fsize)
  194.   long fsize;
  195. {
  196.   char buf[128];
  197.   if (fsize < 128) return 0;
  198.   if (!readblock(buf, 128)) readerr();
  199.   mcopy(&macbinh, &buf[1], sizeof(macbinh));
  200.   if (buf[0] == 0 && macbinh.zero == 0 &&
  201.       macbinh.nlen < 64 && macbinh.nlen > 0 &&
  202.       macbinh.dflen >= 0 && macbinh.rflen >= 0 &&
  203.       (unsigned) (macbinsz(macbinh.dflen) + macbinsz(macbinh.rflen) + 128L)
  204.           <= (unsigned) fsize) return 1;
  205.   fseek(tarf, -128L, 1);
  206.   return 0;
  207. }
  208.  
  209. untar_data (fsize)
  210.   long fsize;
  211. {
  212.   IOParam pb;
  213.   char fname[110];
  214.   int ignore = ignore_file(tarh.name);
  215.  
  216.   if (verbose && !ignore) {
  217.     printf("X %s (%ld bytes)\n", tarh.name, fsize);
  218.     fflush(stdout);
  219.   }
  220.   if (listonly || ignore) {
  221.     fseek(tarf, tarsz(fsize), 1);
  222.     return;
  223.   }
  224.   create_file(tarh.name);
  225.   openpb(&pb, strcpy(fname, tarh.name), fsWrPerm);
  226.   pb.ioReqCount = sizeof(tarh);
  227.   writefork(&pb, fsize, binary);
  228. }
  229.  
  230. untar_macbin (fsize)
  231.   long fsize;
  232. {
  233.   IOParam pb;
  234.   char fname[170], *cp;
  235.   long endpos = ftell(tarf) - 128 + tarsz(fsize);
  236.   int ignore;
  237.   for(cp = tarh.name+strlen(tarh.name); cp != tarh.name && cp[-1] != ':'; --cp);
  238.   mcopy(cp, macbinh.name, macbinh.nlen);
  239.   cp[macbinh.nlen] = '\0';
  240.   ignore = ignore_file(tarh.name);
  241.   if (verbose && !ignore) {
  242.     printf("Macbin %s (%ld+%ld bytes)\n", tarh.name, macbinh.dflen, macbinh.rflen);
  243.     fflush(stdout);
  244.   }
  245.   if (!listonly && !ignore) {
  246.     create_file(tarh.name);
  247.     strcpy(fname, tarh.name);  /* For error messages */
  248.     openpb(&pb, fname, fsWrPerm);
  249.     pb.ioReqCount = 128;
  250.     writefork(&pb, macbinh.dflen, 1);
  251.     pb.ioNamePtr = fname;
  252.     pb.ioVRefNum = 0;
  253.     pb.ioVersNum = 0;
  254.     pb.ioPermssn = fsWrPerm;
  255.     pb.ioMisc = 0;
  256.     if (PBOpenRF((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  257.     pb.ioReqCount = 128;
  258.     writefork(&pb, macbinh.rflen, 1);
  259.     setmacbin(fname);
  260.   }
  261.   fseek(tarf, endpos, 0);
  262. }
  263.  
  264. writefork(pb, size, binp)
  265.   IOParam *pb;
  266.   long size;
  267. {
  268.   int i;
  269.   pb->ioPosMode = fsAtMark;
  270.   pb->ioPosOffset = 0;
  271.   pb->ioBuffer = (Ptr)&tarh;
  272.   while (size) {
  273.     if (!readblock(&tarh, pb->ioReqCount)) {
  274.       PBClose((ParmBlkPtr)pb, 0);
  275.       readerr();
  276.     }
  277.     if (!binp)
  278.       for (i=0; i < pb->ioReqCount; ++i)
  279.         if (tarh.name[i] == 'J'-'@') tarh.name[i] = 'M'-'@';
  280.     if (size < pb->ioReqCount) pb->ioReqCount = size;
  281.     size -= pb->ioReqCount;
  282.     if (PBWrite((ParmBlkPtr)pb, 0) || pb->ioActCount != pb->ioReqCount) {
  283.       int err = pb->ioResult;
  284.       PBClose((ParmBlkPtr)pb, 0);
  285.       pb->ioResult = err;
  286.       pbsyserr(pb);
  287.     }
  288.   }
  289.   PBClose((ParmBlkPtr)pb, 0);
  290. }
  291.  
  292.  
  293. setmacbin (fname)
  294.   char *fname;
  295. {
  296.   FileParam pb;
  297.   pb.ioNamePtr = fname;
  298.   pb.ioVRefNum = 0;
  299.   pb.ioFVersNum = 0;
  300.   pb.ioFDirIndex = 0;
  301.   if (PBGetFInfo((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  302.   mcopy(&pb.ioFlFndrInfo, &macbinh.finfo, sizeof(FInfo));
  303.   if (!ignore_cdate) pb.ioFlCrDat = macbinh.cdate;
  304.   if (!ignore_mdate) pb.ioFlMdDat = macbinh.mdate;
  305.   if (PBSetFInfo((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  306. }
  307.  
  308. untar_directory ()
  309. {
  310.   int len = strlen(tarh.name);
  311.  
  312.   if (len && tarh.name[len-1] != '/') {
  313.     tarh.name[len] = '/';
  314.     tarh.name[len+1] = '\0';
  315.   }
  316.   unix2mac(tarh.name);
  317.   if (!tarh.name[1] || ignore_file(tarh.name)) return;
  318.   if (listonly) {
  319.     printf("Directory \"%s\"\n", tarh.name);
  320.     fflush(stdout);
  321.     return;
  322.   }
  323.   create_directory(tarh.name);
  324. }
  325.  
  326. /*  fname may contain a filename after the last :, it will be ignored */
  327. create_directory (fname)
  328.   char *fname;
  329. {
  330.   HFileParam pb;
  331.   char name[128], *cp, *bp;
  332.   
  333.   cp = fname + strlen(fname);
  334.   for (bp = fname; *bp == ':'; ++bp);
  335.  
  336.   pb.ioNamePtr = (StringPtr) name;
  337.   do {
  338.     while (cp[-1] != ':') --cp;
  339.     if (cp == bp) fatal("%s - Bad directory name", fname);
  340.     strncpy(name+1, fname, name[0] = --cp - fname);
  341.     pb.ioVRefNum = 0;
  342.     pb.ioDirID = 0;
  343.   } while (PBDirCreate((HParmBlkPtr)&pb, 0) == dirNFErr);
  344.   if (pb.ioResult) pbsyserr(&pb);
  345.   while (1) {
  346.     while (*++cp != ':') if (!*cp) return;
  347.     strncpy(name+1, fname, name[0] = cp - fname);
  348.     pb.ioVRefNum = 0;
  349.     pb.ioDirID = 0;
  350.     if (PBDirCreate((HParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  351.   }
  352. }
  353.   
  354. create_file (fname)
  355.   char *fname;
  356. {
  357.   FileParam pb;
  358.   char name[128];
  359.   static int fno = 1;
  360.   
  361.   pb.ioNamePtr = c2pstr(strcpy(name, fname));
  362.   pb.ioVRefNum = 0;
  363.   pb.ioFVersNum = 0;
  364.   if (PBCreate((ParmBlkPtr)&pb, 0)) {
  365.      if (pb.ioResult == dirNFErr) {
  366.          create_directory(fname);
  367.          pb.ioNamePtr = c2pstr(strcpy(name, fname));
  368.          pb.ioVRefNum = 0;
  369.          pb.ioFVersNum = 0;
  370.          if (PBCreate((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  371.      }
  372.      else if (pb.ioResult == dupFNErr) {
  373.          /* most likely a clash because Unix FS is case-sensitive */
  374.         fprintf(stderr, "%s: Warning: Duplicate filename, %s renamed to ", Program, fname);
  375.         sprintf (name, "%s_%d", fname, fno++);
  376.         fprintf(stderr, "%s\n", name);
  377.         strcpy(fname, name);
  378.         pb.ioNamePtr = c2pstr(name);
  379.         if (PBCreate((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  380.     }
  381.     else pbsyserr(&pb);
  382.   }
  383.   pb.ioNamePtr = c2pstr(strcpy(name, fname));
  384.   pb.ioVRefNum = 0;
  385.   pb.ioFVersNum = 0;
  386.   pb.ioFDirIndex = 0;
  387.   if (PBGetFInfo((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  388.   pb.ioNamePtr = c2pstr(strcpy(name, fname));
  389.   pb.ioFlFndrInfo.fdType = TEXT;
  390.   pb.ioFlFndrInfo.fdCreator = filecreator;
  391.   if (PBSetFInfo((ParmBlkPtr)&pb, 0)) pbsyserr(&pb);
  392. }
  393.  
  394. untar_link ()
  395. {
  396.   unix2mac(tarh.name);
  397.   unix2mac(tarh.linkname);
  398.   fprintf(stderr, "%s: Warning: Link %s -> %s (ignored)\n", Program, tarh.name, tarh.linkname);
  399. }
  400.  
  401. /* Convert Unix pathname to a mac pathname.  Do not allow absolute
  402.    names - "/foo/bar/baz" is treated as if it were "foo/bar/baz".
  403.  */
  404. unix2mac (name)
  405.   char *name;
  406. {
  407.   char buf[102];
  408.   char *cp = name, *bp = buf, *op;
  409.   *bp++ = ':';
  410.   op = bp;
  411.   while (1) {
  412.     if (cp[0] == '/') ++cp;
  413.     else if (cp[0] == '.' && cp[1] == '/') cp += 2;
  414.     else if (cp[0] == '.' && cp[1] == '.' && cp[2] == '/') {
  415.       if (op == bp) *bp++ = ':', op = bp;
  416.       else for (--op; op != bp && op[-1] != ':'; --op);
  417.       cp += 3;
  418.     }
  419.     else {
  420.       while (*cp && *cp != '/') if ((*op++ = *cp++) == ':') op[-1] = '/';
  421.       if (!*cp++) break;
  422.       *op++ = ':';
  423.     }
  424.   }
  425.   *op = '\0';
  426.   strcpy(name, buf);
  427. }
  428.  
  429. ignore_file (file)
  430.   char *file;
  431. {
  432.   char **fp = files;
  433.   int *ip = ffound;
  434.   if (!fp) return 0;
  435.   for (; *fp; ++fp, ++ip)
  436.     if (match_file(*fp, file)) {
  437.       ++ip[0];
  438.       return 0;
  439.     }
  440.   return 1;
  441. }
  442.  
  443. /* Name is the user-specified name, file is the actual tarred file */
  444. match_file (name, file)
  445.   char *name, *file;
  446. {
  447.   char *cp;
  448.   if (!strdiff(name, file)) return 1;
  449.   if ((cp = strrchr(file, ':')) && !strdiff(cp+1, name)) return 1;
  450.   if (name[strlen(name)-1] == ':' && strdiff(name, file) >= 0) return 1;
  451.   return 0;
  452. }
  453.  
  454. /* return 0 if the same, 1 if s1 is a substring of s2, -1 otherwise */
  455. strdiff (s1, s2)
  456.   char *s1, *s2;
  457. {
  458.   while (*s1) {
  459.     if (*s1 != *s2) {
  460.       char c1 = *s1;
  461.       if ('A' <= c1 && c1 <= 'Z') c1 |= 0x20;
  462.       if ('a' > c1 || c1 > 'z' || c1 != (*s2 | 0x20)) return -1;
  463.     }
  464.     ++s1, ++s2;
  465.   }
  466.   return (*s2 ? 1 : 0);
  467. }
  468.  
  469. untar_number (cp)
  470.   char *cp;
  471. {
  472.   int neg = 0, num = 0;
  473.   while (*cp == ' ') cp++;
  474.   if (*cp == '-') neg++, cp++;
  475.   while ('0' <= *cp && *cp <= '7') num = (num<<3) + (*cp++ - '0');
  476.   if (neg) num = -num;
  477.   return num;
  478. }
  479.  
  480. openpb(pb, name, perm)
  481.   IOParam *pb;
  482.   char *name;
  483.   int perm;
  484. {
  485.   pb->ioVRefNum = 0;
  486.   pb->ioVersNum = 0;
  487.   pb->ioPermssn = perm;
  488.   pb->ioMisc = 0;
  489.   pb->ioRefNum = 0;
  490.   pb->ioNamePtr = c2pstr(name);
  491.   if (PBOpen((ParmBlkPtr)pb, 0)) pbsyserr(pb);
  492. }
  493.  
  494.  
  495. readerr ()
  496. {
  497.   if (feof(tarf)) fatal("Premature end of file");
  498.   else fatal("Read error");
  499. }
  500.  
  501. pbsyserr (pb)
  502.   IOParam *pb;
  503. {
  504.   int err = pb->ioResult;
  505.   char *name = pb->ioNamePtr;
  506.   p2cstr(name);
  507.   if (err == fnfErr) fatal("%s - File not found", name);
  508.   else if (err == nsvErr) fatal("%s - No such volume", name);
  509.   else if (err == tmfoErr) fatal("%s - Too many files open", name);
  510.   else if (err == permErr) fatal("%s - Permissions error", name);
  511.   else if (err == dupFNErr) fatal("%s - Duplicate filename", name);
  512.   else if (err == eofErr) fatal("%s - Premature end of file", name);
  513.   else fatal("%s - Error #%d", name, err);
  514. }
  515.  
  516. fatal(fmt, arg1, arg2, arg3, arg4, arg5, arg6)
  517.   char *fmt, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6;
  518. {
  519.   if (tarf && tarf != stdin) fclose(tarf);
  520.   if (ffound) free(ffound);
  521.   fflush(stdout);
  522.   fprintf(stderr, "%s: ", Program);
  523.   fprintf(stderr, fmt, arg1, arg2, arg3, arg4, arg5, arg6);
  524.   fprintf(stderr, "\n");
  525.   exit(2);
  526. }
  527.  
  528.